#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/cpufreq.h>
#include <linux/cpu.h>

static void cpu_info_sample(struct cpufreq_freqs *freqs,unsigned int states)
{
    if(freqs == NULL)
        return;
    struct cpufreq_policy *policy;

    printk(KERN_INFO "notification %u of frequency transition to %u kHz\n",states,freqs->new); 

    //policy = per_cpu(cpufreq_cpu_data,freqs->cpu);
    policy = cpufreq_cpu_get(freqs->cpu);
    switch(states)
    {
        case CPUFREQ_PRECHANGE:
            if(policy)
                printk(KERN_INFO "before change cpu frequency is %u and cpufreq assumed %u kHz\n",freqs->old,policy->cur);
            break;

        case CPUFREQ_POSTCHANGE:
            if(policy)
            {
                printk(KERN_INFO "after change, cpu frequency is %u and on cpu %u\n",freqs->new,freqs->cpu);
            }
            break;
    }
    cpufreq_cpu_put(policy);
    jprobe_return();
    return;
}

static struct jprobe my_jprobe = {
    .entry          = cpu_info_sample,
    .kp = {
        .symbol_name    = "cpufreq_notify_transition",
    },
};

static int __init jprobe_init(void)
{
    int ret;

    ret = register_jprobe(&my_jprobe);
    if (ret < 0) {
        printk(KERN_INFO "register_jprobe failed, returned %d\n", ret);
        return -1;
    }
    printk(KERN_INFO "Planted jprobe at %p, handler addr %p\n",
           my_jprobe.kp.addr, my_jprobe.entry);
    return 0;
}

static void __exit jprobe_exit(void)
{
    unregister_jprobe(&my_jprobe);
    printk(KERN_INFO "jprobe at %p unregistered\n", my_jprobe.kp.addr);
}

module_init(jprobe_init)
module_exit(jprobe_exit)
MODULE_LICENSE("GPL");

